package com.zhilei.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.zhilei.base.BaseInfoProperties;
import com.zhilei.enums.CompanyReviewStatus;
import com.zhilei.enums.YesOrNo;
import com.zhilei.exceptions.GraceException;
import com.zhilei.grace.result.ResponseStatusEnum;
import com.zhilei.mapper.CompanyMapper;
import com.zhilei.mapper.CompanyMapperCustom;
import com.zhilei.mapper.CompanyPhotoMapperCustom;
import com.zhilei.pojo.Company;
import com.zhilei.pojo.CompanyPhoto;
import com.zhilei.pojo.bo.CreateCompanyBO;
import com.zhilei.pojo.bo.ModifyCompanyInfoBO;
import com.zhilei.pojo.bo.QueryCompanyBO;
import com.zhilei.pojo.bo.ReviewCompanyBO;
import com.zhilei.pojo.vo.CompanyInfoVO;
import com.zhilei.service.CompanyService;
import com.zhilei.untils.PagedGridResult;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 * 企业表 服务实现类
 * </p>
 *
 * @author 志磊
 * @since 2024-09-20
 */
@Service
public class CompanyServiceImpl extends BaseInfoProperties implements CompanyService {

    private final Timer timer = new Timer();
    @Autowired
    private CompanyMapper companyMapper;
    @Autowired
    private CompanyMapperCustom companyMapperCustom;
    @Autowired
    private CompanyPhotoMapperCustom companyPhotoMapperCustom;
    private ReentrantLock reentrantLock;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 根据公司名称查询公司是否存在
     *
     * @param fullName
     * @return
     */
    @Override
    public Company getByFullName(String fullName) {
        Company company = companyMapper.selectOne(new QueryWrapper<Company>().eq("company_name", fullName));
        return company;
    }

    /**
     * 创建公司
     *
     * @param createCompanyBO
     */
    @Transactional
    @Override
    public String createNewCompany(CreateCompanyBO createCompanyBO) {
        Company byFullName = this.getByFullName(createCompanyBO.getCompanyName());
        if (byFullName != null) GraceException.disPlay(ResponseStatusEnum.FAILED);
        Company company = new Company();
        BeanUtils.copyProperties(createCompanyBO, company);
        company.setReviewStatus(CompanyReviewStatus.NOTHING.type);
        company.setIsVip(YesOrNo.NO.type);
        company.setCreatedTime(LocalDateTime.now());
        company.setUpdatedTime(LocalDateTime.now());
        companyMapper.insert(company);
        return company.getId();
    }

    /**
     * 修改审核失败的数据，发起审核
     *
     * @param createCompanyBO
     * @return
     */
    @Transactional
    @Override
    public String resetCompany(CreateCompanyBO createCompanyBO) {
        Company byFullName = this.getByFullName(createCompanyBO.getCompanyName());
        if (byFullName != null) GraceException.disPlay(ResponseStatusEnum.FAILED);

        Company company = new Company();
        BeanUtils.copyProperties(createCompanyBO, company);

        company.setId(createCompanyBO.getCompanyId());
        company.setReviewStatus(CompanyReviewStatus.NOTHING.type);
        company.setUpdatedTime(LocalDateTime.now());

        companyMapper.updateById(company);
        return company.getId();
    }

    /**
     * 提交审核
     *
     * @param reviewCompanyBO
     */
    @Transactional
    @Override
    public void SubmitForReview(ReviewCompanyBO reviewCompanyBO) {
        // TODO 获取企业id
        String companyId = reviewCompanyBO.getCompanyId();
        Company company = companyMapper.selectOne(new QueryWrapper<Company>().eq("id", companyId));
        if (company == null) {
            GraceException.disPlay(ResponseStatusEnum.FAILED);
        }

        // TODO 提交的授权书
        company.setAuthLetter(reviewCompanyBO.getAuthLetter());
        // TODO 提交人的用户id
        company.setCommitUserId(reviewCompanyBO.getHrUserId());
        // TODO 提交人的手机号
        company.setCommitUserMobile(reviewCompanyBO.getHrMobile());
        // TODO 审核的状态
        company.setReviewStatus(CompanyReviewStatus.REVIEW_ING.type);
        // TODO 设置审核内容
        company.setReviewReplay("");
        // TODO 提交审核日期
        company.setCommitDate(LocalDate.now());
        company.setUpdatedTime(LocalDateTime.now());

        companyMapper.updateById(company);
    }

    /**
     * 根据用户表绑定的企业id查询企业表的最新数据
     *
     * @param hrInWhichCompanyId
     * @return
     */
    @Override
    public Company getHrInWhichCompany(String hrInWhichCompanyId) {
        Company company = companyMapper.selectOne(new QueryWrapper<Company>().eq("id", hrInWhichCompanyId));
        return company;
    }

    /**
     * 运营平台查询企业信息
     *
     * @param asQueryCompany
     * @param page
     * @param limit
     * @return
     */
    @Override
    public PagedGridResult getCompanyList(QueryCompanyBO asQueryCompany, Integer page, Integer limit) {

        PageHelper.startPage(page, limit);

        HashMap<String, Object> map = new HashMap<>();
        map.put("companyName", asQueryCompany.getCompanyName());
        map.put("realName", asQueryCompany.getCommitUser());
        map.put("reviewStatus", asQueryCompany.getReviewStatus());
        map.put("commitDateStart", asQueryCompany.getCommitDateStart());
        map.put("commitDateEnd", asQueryCompany.getCommitDateEnd());
        List<CompanyInfoVO> companyInfoVOS = companyMapperCustom.queryCompanyList(map);

        PagedGridResult pagedGridResult = setterPagedGrid(companyInfoVOS, page);

        return pagedGridResult;
    }

    /**
     * 修改企业简称，基于redisson的方式实现分布式锁
     *
     * @param modifyCompanyInfoBO
     */
    @Transactional
    @Override
    public void updateCompanyShortName(ModifyCompanyInfoBO modifyCompanyInfoBO) {

        // 使用redisson实现分布式锁

        // 1. 首先获得锁
        String distLock = "redisson-lock";
        // 获取倒是哪一把锁
        RLock lock = redissonClient.getLock(distLock);
        // 加锁
        lock.lock();

        try {
            // 使用 while 循环去解耦递归调用
            System.out.println("正在加锁，请稍后去执行！！！");
            Thread.sleep(40000);
            // 执行当前业务代码
            this.doModify(modifyCompanyInfoBO);
        } catch (Exception e) {
            GraceException.disPlay(ResponseStatusEnum.FAILED);
        } finally {
            // 2. 释放锁
            lock.unlock();
        }
    }

    /**
     * 修改企业简称，使用基于redis的方式
     *
     * @param modifyCompanyInfoBO
     */
    @Transactional
    // @Override
    public void updateCompanyShortName2(ModifyCompanyInfoBO modifyCompanyInfoBO) {

        // 使用redis实现分布式锁

        // 1. 首先获得锁
        String distLock = "redis-lock";

        // 1.1 给锁添加过期时间，到时间 ttl 自动释放锁,保证操作的原子性，要么全部成功，要么全部失败
        // 使用uuid生成随机数来替换setnx的固定值
        String selfName = UUID.randomUUID().toString();
        Long expireTime = 30L;
        Boolean b = redis.setnx(distLock, selfName, 30);

        // 使用 while 循环去解耦递归调用
        while (b) {
            System.out.println("正在加锁，请稍后去执行！！！");
            // 休眠当前线程
            try {
                Thread.sleep(50);
            } catch (Exception e) {
                GraceException.disPlay(ResponseStatusEnum.FAILED);
            }
        }

        // 自动刷新当前休眠时间
        autoRefreshLuaScript(distLock, selfName, expireTime);

        // 执行业务代码
        this.doModify(modifyCompanyInfoBO);

        // 定义LUA脚本去实现原子性操作
        String scriptLock =
                " if redis.call('get',KEYS[1]) == ARGV[1] " +
                        " then " +
                        " return redis.call('del',KEYS[1]) " +
                        " else " +
                        " return 0 " +
                        "end";

        Long luaScript = redis.execLuaScript(scriptLock, distLock, selfName);
        // 当返回的是0的时候，证明删除失败了；否则返回的是1
        if (luaScript == 1L) {

            System.out.println("解锁成功！");
            // 解锁后，重置锁的失效时间
            timer.cancel();
        }

    }

    /**
     * 重置锁的失效时间，避免当业务代码处理时间过长而导致失效
     *
     * @param distLock
     * @param selfName
     * @param expireTime
     */
    private void autoRefreshLuaScript(String distLock, String selfName, Long expireTime) {
        // 定义LUA脚本去实现原子性操作
        // 修改为 expire 当访问或修改具有过期时间的密钥时，将重置过期时间
        String checkLock =
                " if redis.call('get',KEYS[1]) == ARGV[1] " +
                        " then " +
                        " return redis.call('expire',KEYS[1]) " +
                        " else " +
                        " return 0 " +
                        "end";

        // timer 处理当前key的时间
        timer.schedule(new TimerTask() {
                           @Override
                           public void run() {
                               // 调用刷新的LUA脚本
                               redis.execLuaScript(checkLock, distLock, selfName);
                           }
                       },
                expireTime / 3 * 1000,
                expireTime / 3 * 1000);

    }


    /**
     * 抽取修改企业简称的代码
     *
     * @param modifyCompanyInfoBO
     */
    private void doModify(ModifyCompanyInfoBO modifyCompanyInfoBO) {
        String companyId = modifyCompanyInfoBO.getCompanyId();
        Company company = this.getById(companyId);
        BeanUtils.copyProperties(modifyCompanyInfoBO, company);
        company.setId(companyId);
        company.setUpdatedTime(LocalDateTime.now());
        companyMapper.updateById(company);
        redis.del(REDIS_COMPANY_BASE_INFO + ":" + companyId);
        redis.del(REDIS_COMPANY_MORE_INFO + ":" + companyId);
    }

    /**
     * 审核企业成功
     *
     * @param reviewCompanyBO
     */
    @Transactional
    @Override
    public void updateReviewCompany(ReviewCompanyBO reviewCompanyBO) {
        Company company = new Company();
        // 根据id及逆行修改
        company.setId(reviewCompanyBO.getCompanyId());
        // 设置审核状态
        company.setReviewStatus(reviewCompanyBO.getReviewStatus());
        // 设置审核意见
        company.setReviewReplay(reviewCompanyBO.getReviewReplay());
        // 设置审核时间
        company.setUpdatedTime(LocalDateTime.now());

        companyMapper.updateById(company);
    }

    /**
     * 根据企业id查询企业信息
     *
     * @param companyId
     * @return
     */
    @Override
    public Company getById(String companyId) {
        Company company = companyMapper.selectById(companyId);
        return company;
    }

    /**
     * 根据企业id查询企业相册
     *
     * @param companyId
     */
    @Override
    public CompanyPhoto getCompanyPhotos(String companyId) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("companyId", companyId);
        return companyPhotoMapperCustom.getCompanyPhotos(map);
    }

}
